package com.ssau.expert.core.impl;

import com.intelli.ray.core.ManagedComponent;
import com.ssau.expert.app.AppContext;
import com.ssau.expert.common.InputParameters;
import com.ssau.expert.common.RuleDTO;
import com.ssau.expert.core.api.DatabaseCoreAPI;
import com.ssau.expert.core.model.FuzzyRule;
import com.ssau.expert.core.model.Rule;
import com.ssau.expert.core.util.RuleDTOParser;
import com.ssau.expert.entity.Ship;
import com.ssau.expert.entity.ShipProfile;
import com.ssau.expert.exception.ShipMergeException;
import com.ssau.expert.util.FileIO;

import java.util.*;

/**
 * Author: Sergey Saiyan
 * Created: 05.09.13 13:44
 */
@ManagedComponent(name = "Database")
public class DatabaseImpl implements DatabaseCoreAPI {

    private List<Ship> ships = null;

    private Map<String, Rule> ruleMap = new LinkedHashMap<>();

    @Override
    public List<Ship> getShips() {
        return new ArrayList<>(ships);
    }

    @Override
    public List<ShipProfile> getShipProfiles() {
        return Arrays.asList(ShipProfile.values());
    }

    @Override
    public void putRule(Rule rule) {
        ruleMap.put(rule.getId(), rule);
    }

    @Override
    public Collection<Rule> getRuleCollection() {
        return ruleMap.values();
    }

    @Override
    public Collection<FuzzyRule> getFuzzyRules() {
        Collection<Rule> rules = getRuleCollection();

        if (rules.isEmpty()) {
            return Collections.emptyList();
        }

        Rule rule = rules.iterator().next();
        if (!(rule instanceof FuzzyRule)) {
            throw new IllegalStateException("Rules are not fuzzy");
        }

        List<FuzzyRule> fuzzyRules = new ArrayList<>(rules.size());
        for (Rule rule_ : rules) {
            fuzzyRules.add((FuzzyRule) rule_);
        }
        return fuzzyRules;
    }

    @Override
    public void restoreRules(Collection<Rule> rules) {
        ruleMap.clear();
        for (Rule rule : rules) {
            ruleMap.put(rule.getId(), rule);
        }
    }

    @Override
    public void clearRules() {
        ruleMap.clear();
    }

    @Override
    public List<String> getRules() {
        if (ruleMap.isEmpty()) {
            ExpertSystem expertSystem = AppContext.get().getBean(ExpertSystem.class);
            List<RuleDTO> list = expertSystem.getExpertCoeffs();
            for (RuleDTO ruleDTO : list) {
                FuzzyRule[] rules = RuleDTOParser.parse(ruleDTO);

                putRule(rules[0]);
                //putRule(rules[1]);
            }
        }

        List<String> output = new ArrayList<>();
        for (Rule rule : ruleMap.values()) {
            output.add(rule.getRuleString());
        }
        return output;
    }

    @Override
    public void mergeShip(Ship ship, InputParameters params) throws ShipMergeException {
        if (params.maxDistance1 == null || params.maxDistance1 == 0) {
            throw new ShipMergeException("Max distance 1 is null or zero");
        }
        if (params.staff1 == null || params.staff1 == 0) {
            throw new ShipMergeException("Staff is null or zero");
        }
        if (params.cfMaxDistance1 == null || params.cfMaxDistance1 <= 0) {
            throw new ShipMergeException("Max distance CF1 is null or non-positive");
        }
        if (params.cfStaff1 == null || params.cfStaff1 <= 0) {
            throw new ShipMergeException("Staff cf1 is null or non-positive");
        }
        if (params.maxDistance2 != null) {
            if (params.maxDistance2 <= 0) {
                throw new ShipMergeException("Max distance 2 must be a positive number");
            }
            if (params.cfMaxDistance2 == null || params.cfMaxDistance2 <= 0) {
                throw new ShipMergeException("Max distance CF2 is null or non-positive");
            }
            if (params.cfMaxDistance1 + params.cfMaxDistance2 > 100) {
                throw new ShipMergeException("Max distance CF sum is greater then 100");
            }
        }
        if (params.staff2 != null) {
            if (params.staff2 <= 0) {
                throw new ShipMergeException("Staff 2 must be a positive number");
            }
            if (params.cfStaff2 == null || params.cfStaff2 <= 0) {
                throw new ShipMergeException("Staff CF2 is null or non-positive");
            }
            if (params.cfStaff1 + params.cfStaff2 > 100) {
                throw new ShipMergeException("Staff CF sum is greater then 100");
            }
        }

        for (Ship dbShip : ships) {
            if (dbShip.getShipName() == ship.getShipName()) {
                dbShip.setMaxDistance1(params.maxDistance1);
                dbShip.setCfMaxDistance1(params.cfMaxDistance1);

                if (params.maxDistance2 != null)
                    dbShip.setMaxDistance2(params.maxDistance2);

                if (params.cfMaxDistance2 != null)
                    dbShip.setCfMaxDistance2(params.cfMaxDistance2);

                dbShip.setMaxStaff1(params.staff1);
                dbShip.setCfMaxStaff1(params.cfStaff1);

                if (params.staff2 != null)
                    dbShip.setMaxStaff2(params.staff2);

                if (params.cfStaff2 != null)
                    dbShip.setCfMaxStaff2(params.cfStaff2);
            }
        }
        flush();
    }

    public static DatabaseImpl get() {
        return AppContext.get().getBean(DatabaseImpl.class);
    }

    @Override
    public void init() {
        ships = FileIO.loadShips();
    }

    @Override
    public void flush() {
        FileIO.saveShips(ships);
    }
}
